#include "StdAfx.h"
#include "SimpleMeshCreator.h"

#include "AppData.h"
//#include "InputLayouts.h"
#include "Vertex.h"
#include "Mesh.h"

using namespace mgfx;
using namespace std;

FORCEINLINE bool XMF3_EqualWithEps(XMFLOAT3 f1, XMFLOAT3 f2, float eps = 0.01f)
{
	return sqrtf( (f1.x - f2.x)*(f1.x - f2.x) + (f1.y - f2.y)*(f1.y - f2.y) + (f1.z - f2.z)*(f1.z - f2.z) ) < 0.01f;
}

template <typename INDEX_TYPE>
FORCEINLINE INDEX_TYPE findAdjacentVertex(INDEX_TYPE * indices, SIZE_T indCount, Vertex * vertices, SIZE_T vertCount, INDEX_TYPE i0, INDEX_TYPE i1, uint triInd)
{
	INDEX_TYPE ni0, ni1, ni2;
	uint triIndex = 0;
	for(INDEX_TYPE i=0 ; i<indCount ; i+=6, ++triIndex)
	{
		if(triInd == triIndex)
			continue;

		ni0 = indices[i+0];
		ni1 = indices[i+2];
		ni2 = indices[i+4];

		if( (i0 == ni0 && i1 == ni1) || (i0 == ni1 && i1 == ni0) )
			return ni2;
		if( (i0 == ni1 && i1 == ni2) || (i0 == ni2 && i1 == ni1) )
			return ni0;
		if( (i0 == ni0 && i1 == ni2) || (i0 == ni2 && i1 == ni0) )
			return ni1;
	}

	XMFLOAT3 v0, v1;
	v0 = vertices[i0].pos;
	v1 = vertices[i1].pos;

	XMFLOAT3 nv0, nv1, nv2;

	triIndex = 0;
	for(INDEX_TYPE i=0 ; i<indCount ; i+=6, ++triIndex)
	{
		if(triInd == triIndex)
			continue;

		ni0 = indices[i+0];
		ni1 = indices[i+2];
		ni2 = indices[i+4];

		nv0 = vertices[ni0].pos;
		nv1 = vertices[ni1].pos;
		nv2 = vertices[ni2].pos;

		if( ( XMF3_EqualWithEps(v0, nv0) && XMF3_EqualWithEps(v1, nv1) ) || ( XMF3_EqualWithEps(v0, nv1) && XMF3_EqualWithEps(v1, nv0) ) )
			return ni2;
		if( ( XMF3_EqualWithEps(v0, nv1) && XMF3_EqualWithEps(v1, nv2) ) || ( XMF3_EqualWithEps(v0, nv2) && XMF3_EqualWithEps(v1, nv1) ) )
			return ni0;
		if( ( XMF3_EqualWithEps(v0, nv0) && XMF3_EqualWithEps(v1, nv2) ) || ( XMF3_EqualWithEps(v0, nv2) && XMF3_EqualWithEps(v1, nv0) ) )
			return ni1;
	}

	return 0;
}

// indexBuffer - already filled with adjacency. Function should just correct it.
// TODO: http://forum.gamedev.pl/index.php/topic,20013.msg238169
template <typename INDEX_TYPE>
void generateAdjacency(ID3DX10Mesh * mesh)
{
	ID3DX10MeshBuffer * indexBuffer;
	HR(mesh->GetIndexBuffer(&indexBuffer));

	INDEX_TYPE * indices;
	SIZE_T indCount;
	HR(indexBuffer->Map((void**)&indices, &indCount));
	indCount = indCount / sizeof(INDEX_TYPE);


	ID3DX10MeshBuffer * vertexBuffer;
	HR(mesh->GetVertexBuffer(0, &vertexBuffer));

	Vertex * vertices;
	SIZE_T vertCount;
	HR(vertexBuffer->Map((void**)&vertices, &vertCount));
	vertCount = vertCount / sizeof(Vertex);

	//SIZE_T vertSize

	uint triIndex = 0;
	for(INDEX_TYPE i=0 ; i<indCount ; i+=6, ++triIndex)
	{
		indices[i+1] = findAdjacentVertex(indices, indCount, vertices, vertCount, indices[i+0], indices[i+2], triIndex);
		indices[i+3] = findAdjacentVertex(indices, indCount, vertices, vertCount, indices[i+2], indices[i+4], triIndex);
		indices[i+5] = findAdjacentVertex(indices, indCount, vertices, vertCount, indices[i+4], indices[i+0], triIndex);
	}

	HR(indexBuffer->Unmap());
	HR(vertexBuffer->Unmap());

	ReleaseCOM(indexBuffer);
	ReleaseCOM(vertexBuffer);
}

Mesh * SimpleMeshCreator::createSphere(float radius, ushort numSlices, ushort numStacks)
{
	ID3D10Device * device = AppData::Instance.Device;
	ID3DX10Mesh * mesh;

	vector<Vertex> vertices;
	vector<ushort> indices;

	float phiStep = PI/numStacks;

	// do not count the poles as rings
	ushort numRings = numStacks-1;

	// Compute vertices for each stack ring.
	for(ushort i = 1; i <= numRings; ++i)
	{
		float phi = i*phiStep;

		// vertices of ring
		float thetaStep = 2.0f*PI/numSlices;
		for(ushort j = 0; j <= numSlices; ++j)
		{
			float theta = j*thetaStep;

			Vertex v;

			// spherical to cartesian
			v.pos.x = radius*sinf(phi)*cosf(theta);
			v.pos.y = radius*cosf(phi);
			v.pos.z = radius*sinf(phi)*sinf(theta);

			// partial derivative of P with respect to theta
			v.tangent.x = -radius*sinf(phi)*sinf(theta);
			v.tangent.y = 0.0f;
			v.tangent.z = radius*sinf(phi)*cosf(theta);

			XMVECTOR normal, pos;
			pos = XMLoadFloat3(&v.pos);
			normal = XMVector3Normalize(pos);
			XMStoreFloat3(&v.normal, normal);

			v.texC.x = theta / (2.0f*PI);
			v.texC.y = phi / PI;
			//v.texC.y = 1.0f - phi / PI;

			vertices.push_back( v );
		}
	}

	// poles: note that there will be texture coordinate distortion
	vertices.push_back( Vertex(0.0f, -radius, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f) );
	vertices.push_back( Vertex(0.0f, radius, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f) );

	ushort northPoleIndex = (uint)vertices.size()-1;
	ushort southPoleIndex = (uint)vertices.size()-2;

	ushort numRingVertices = numSlices+1;

	// Compute indices for inner stacks (not connected to poles).
	for(ushort i = 0; i < numStacks-2; ++i)
	{
		for(ushort j = 0; j < numSlices; ++j)
		{
			indices.push_back(i*numRingVertices + j);
			indices.push_back(i*numRingVertices + j+1);
			indices.push_back((i+1)*numRingVertices + j);

			indices.push_back((i+1)*numRingVertices + j);
			indices.push_back(i*numRingVertices + j+1);
			indices.push_back((i+1)*numRingVertices + j+1);
		}
	}

	// Compute indices for top stack.  The top stack was written 
	// first to the vertex buffer.
	for(ushort i = 0; i < numSlices; ++i)
	{
		indices.push_back(northPoleIndex);
		indices.push_back(i+1);
		indices.push_back(i);
	}

	// Compute indices for bottom stack.  The bottom stack was written
	// last to the vertex buffer, so we need to offset to the index
	// of first vertex in the last ring.
	UINT baseIndex = (numRings-1)*numRingVertices;
	for(ushort i = 0; i < numSlices; ++i)
	{
		indices.push_back(southPoleIndex);
		indices.push_back(baseIndex+i);
		indices.push_back(baseIndex+i+1);
	}

	ushort numVertices = (ushort)vertices.size();
	ushort numTriangles    = (ushort)indices.size()/3;


	D3D10_INPUT_ELEMENT_DESC vertexDesc[] =
	{
		{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"TANGENT",  0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 36, D3D10_INPUT_PER_VERTEX_DATA, 0},

	};

	HR(D3DX10CreateMesh(device, vertexDesc, 4, 
		vertexDesc[0].SemanticName, numVertices, 
		numTriangles, 0, &mesh));

	
	HR(mesh->SetVertexData(0, &vertices[0]));

	vector<uint> attributeData;
	for(uint i=0 ; i<numTriangles ; ++i)
		attributeData.push_back(0);

	HR(mesh->SetIndexData(&indices[0], numTriangles*3));
	HR(mesh->SetAttributeData(&attributeData[0]));

	HR(mesh->GenerateAdjacencyAndPointReps(0.001f));
	HR(mesh->Optimize(D3DX10_MESHOPT_ATTR_SORT|D3DX10_MESHOPT_VERTEX_CACHE,0,0));
	HR(mesh->GenerateGSAdjacency());
	generateAdjacency<ushort>(mesh);
	HR(mesh->CommitToDevice());
	

	Mesh * myMesh = new Mesh(mesh, numTriangles*6);

	return myMesh;
}

Mesh * SimpleMeshCreator::createQuad(ushort n, ushort m, float dx, float texWrapX, float texWrapY)
{
	ID3D10Device * device = AppData::Instance.Device;
	ID3DX10Mesh * mesh;

	ushort numVertices = m*n;
	ushort numFaces    = (m-1)*(n-1)*2;

	vector<Vertex> vertices(numVertices);
	vector<ushort> indices(numFaces * 3);


	float halfWidth = (n-1)*dx*0.5f;
	float halfDepth = (m-1)*dx*0.5f;

	float du = 1.0f / (n-1);
	float dv = 1.0f / (m-1);
	for(ushort i = 0; i < m; ++i)
	{
		float z = halfDepth - i*dx;
		for(ushort j = 0; j < n; ++j)
		{
			float x = -halfWidth + j*dx;

			vertices[i*n+j].pos     = XMFLOAT3(x, 0.0f, z);
			vertices[i*n+j].normal  = XMFLOAT3(0.0f, 1.0f, 0.0f);
			vertices[i*n+j].tangent = XMFLOAT3(1.0f, 0.0f, 0.0f);

			// Stretch texture over grid.
			float texCx = texWrapX*j*du;
			//while(texCx > 1.0f) texCx -= 1.0f;

			float texCy = texWrapY*i*dv;
			//while(texCy > 1.0f) texCy -= 1.0f;

			vertices[i*n+j].texC.x = texCx;
			vertices[i*n+j].texC.y = texCy;
		}
	}



	int k = 0;
	for(ushort i = 0; i < m-1; ++i)
	{
		for(ushort j = 0; j < n-1; ++j)
		{
			indices[k]   = i*n+j;
			indices[k+1] = i*n+j+1;
			indices[k+2] = (i+1)*n+j;

			indices[k+3] = (i+1)*n+j;
			indices[k+4] = i*n+j+1;
			indices[k+5] = (i+1)*n+j+1;

			k += 6; // next quad
		}
	}


	D3D10_INPUT_ELEMENT_DESC vertexDesc[] =
	{
		{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"TANGENT",  0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 36, D3D10_INPUT_PER_VERTEX_DATA, 0},

	};

	HR(D3DX10CreateMesh(device, vertexDesc, 4, 
		vertexDesc[0].SemanticName, numVertices, 
		numFaces, 0, &mesh));

	
	HR(mesh->SetVertexData(0, &vertices[0]));

	vector<uint> attributeData;
	for(uint i=0 ; i<numFaces ; ++i)
		attributeData.push_back(0);

	HR(mesh->SetIndexData(&indices[0], numFaces*3));
	HR(mesh->SetAttributeData(&attributeData[0]));

	HR(mesh->GenerateAdjacencyAndPointReps(0.001f));
	HR(mesh->Optimize(D3DX10_MESHOPT_ATTR_SORT|D3DX10_MESHOPT_VERTEX_CACHE,0,0));
	HR(mesh->GenerateGSAdjacency());
	HR(mesh->CommitToDevice());

	Mesh * myMesh = new Mesh(mesh, numFaces*6);

	return myMesh;
}

Mesh * SimpleMeshCreator::createBox(float scale, float texWrapX, float texWrapY)
{
	ID3D10Device * device = AppData::Instance.Device;
	ID3DX10Mesh * mesh;


	ushort numVertices = 24;
	ushort numFaces    = 12; // 2 per quad

	// Create vertex buffer
    Vertex v[24];
    
	// Fill in the front face vertex data.
	v[0] = Vertex(-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, texWrapY);
	v[1] = Vertex(-0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
	v[2] = Vertex( 0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, texWrapX, 0.0f);
	v[3] = Vertex( 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, texWrapX, texWrapY);

	// Fill in the back face vertex data.
	v[4] = Vertex(-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, texWrapX, texWrapY);
	v[5] = Vertex( 0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, texWrapY);
	v[6] = Vertex( 0.5f,  0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
	v[7] = Vertex(-0.5f,  0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, texWrapX, 0.0f);

	// Fill in the top face vertex data.
	v[8]  = Vertex(-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, texWrapY);
	v[9]  = Vertex(-0.5f, 0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
	v[10] = Vertex( 0.5f, 0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, texWrapX, 0.0f);
	v[11] = Vertex( 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, texWrapX, texWrapY);

	// Fill in the bottom face vertex data.
	v[12] = Vertex(-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, texWrapX, texWrapY);
	v[13] = Vertex( 0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, texWrapY);
	v[14] = Vertex( 0.5f, -0.5f,  0.5f, -1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f);
	v[15] = Vertex(-0.5f, -0.5f,  0.5f, -1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, texWrapX, 0.0f);

	// Fill in the left face vertex data.
	v[16] = Vertex(-0.5f, -0.5f,  0.5f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, texWrapY);
	v[17] = Vertex(-0.5f,  0.5f,  0.5f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
	v[18] = Vertex(-0.5f,  0.5f, -0.5f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, texWrapX, 0.0f);
	v[19] = Vertex(-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, texWrapX, texWrapY);

	// Fill in the right face vertex data.
	v[20] = Vertex( 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, texWrapY);
	v[21] = Vertex( 0.5f,  0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f);
	v[22] = Vertex( 0.5f,  0.5f,  0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, texWrapX, 0.0f);
	v[23] = Vertex( 0.5f, -0.5f,  0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, texWrapX, texWrapY);

	// Scale the box.
	for(ushort i = 0; i < numVertices; ++i)
	{
		XMVECTOR pos = XMLoadFloat3(&v[i].pos);
		XMVECTOR sca = XMVectorReplicate(scale);
		pos = XMVectorMultiply(pos, sca);
		XMStoreFloat3(&v[i].pos, pos);
		//v[i].pos *= scale;
		//v[i].texC.y = 1.0f - v[i].texC.y;
	}


	// Create the index buffer

	ushort i[36];

	// Fill in the front face index data
	i[0] = 0; i[1] = 1; i[2] = 2;
	i[3] = 0; i[4] = 2; i[5] = 3;

	// Fill in the back face index data
	i[6] = 4; i[7]  = 5; i[8]  = 6;
	i[9] = 4; i[10] = 6; i[11] = 7;

	//// Fill in the top face index data
	//i[12] = 3; i[13] =  2; i[14] = 6;
	//i[15] = 3; i[16] = 6; i[17] = 5;

	//// Fill in the bottom face index data
	//i[18] = 0; i[19] = 4; i[20] = 7;
	//i[21] = 0; i[22] = 7; i[23] = 1;

	//// Fill in the left face index data
	//i[24] = 1; i[25] = 7; i[26] = 6;
	//i[27] = 1; i[28] = 6; i[29] = 2;

	//// Fill in the right face index data
	//i[30] = 0; i[31] = 3; i[32] = 5;
	//i[33] = 0; i[34] = 5; i[35] = 4;

	// Fill in the top face index data
	i[12] = 8; i[13] =  9; i[14] = 10;
	i[15] = 8; i[16] = 10; i[17] = 11;

	// Fill in the bottom face index data
	i[18] = 12; i[19] = 13; i[20] = 14;
	i[21] = 12; i[22] = 14; i[23] = 15;

	// Fill in the left face index data
	i[24] = 16; i[25] = 17; i[26] = 18;
	i[27] = 16; i[28] = 18; i[29] = 19;

	// Fill in the right face index data
	i[30] = 20; i[31] = 21; i[32] = 22;
	i[33] = 20; i[34] = 22; i[35] = 23;


	D3D10_INPUT_ELEMENT_DESC vertexDesc[] =
	{
		{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"TANGENT",  0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 36, D3D10_INPUT_PER_VERTEX_DATA, 0},

	};

	HR(D3DX10CreateMesh(device, vertexDesc, 4, 
		vertexDesc[0].SemanticName, numVertices, 
		numFaces, 0, &mesh));

	
	HR(mesh->SetVertexData(0, &v[0]));

	vector<uint> attributeData;
	for(uint i=0 ; i<numFaces ; ++i)
		attributeData.push_back(0);

	HR(mesh->SetIndexData(&i[0], numFaces*3));
	HR(mesh->SetAttributeData(&attributeData[0]));

	HR(mesh->GenerateAdjacencyAndPointReps(0.001f));
	HR(mesh->Optimize(D3DX10_MESHOPT_ATTR_SORT|D3DX10_MESHOPT_VERTEX_CACHE,0,0));
	HR(mesh->GenerateGSAdjacency());
	generateAdjacency<ushort>(mesh);
	HR(mesh->CommitToDevice());

	Mesh * myMesh = new Mesh(mesh, numFaces*6);

	return myMesh;
}